/**
 * @file aiot_itcp_api.c
 * @brief ITCP模块实现, 其中包含了向物联网平台认证和上报数据的API接口
 * @date 2019-12-27
 *
 * @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
 *
 */

#include "core_itcp.h"

static void _core_itcp_exec_inc(core_itcp_handle_t *itcp_handle)
{
    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
    itcp_handle->core_exec_count++;
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);
}

static void _core_itcp_exec_dec(core_itcp_handle_t *itcp_handle)
{
    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
    itcp_handle->core_exec_count--;
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);
}

static int32_t _core_itcp_sysdep_return(int32_t sysdep_code, int32_t core_code)
{
    if (sysdep_code >= (STATE_PORT_BASE - 0x00FF) && sysdep_code < (STATE_PORT_BASE)) {
        return sysdep_code;
    } else {
        return core_code;
    }
}

static int32_t _core_itcp_connect(core_itcp_handle_t *itcp_handle)
{
    int32_t res = STATE_SUCCESS;

    /* disconnect first if network is already established */
    if (itcp_handle->network_handle != NULL) {
        itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
    }

    /* establish network connection */
    core_sysdep_socket_type_t socket_type = CORE_SYSDEP_SOCKET_TCP_CLIENT;

    if (itcp_handle->host == NULL) {
        return STATE_USER_INPUT_MISSING_HOST;
    }

    itcp_handle->network_handle = itcp_handle->sysdep->core_sysdep_network_init();
    if (itcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_MALLOC_FAILED;
    }

    if(NULL != itcp_handle->eth) {
        if ((res = itcp_handle->sysdep->core_sysdep_network_setopt(itcp_handle->network_handle, CORE_SYSDEP_NETWORK_ETH,
                    itcp_handle->eth)) < 0) {
            itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
        }
    }

    if ((res = itcp_handle->sysdep->core_sysdep_network_setopt(itcp_handle->network_handle, CORE_SYSDEP_NETWORK_SOCKET_TYPE,
                &socket_type)) < 0 ||
        (res = itcp_handle->sysdep->core_sysdep_network_setopt(itcp_handle->network_handle, CORE_SYSDEP_NETWORK_HOST,
                itcp_handle->host)) < 0 ||
        (res = itcp_handle->sysdep->core_sysdep_network_setopt(itcp_handle->network_handle, CORE_SYSDEP_NETWORK_PORT,
                &itcp_handle->port)) < 0 ||
        (res = itcp_handle->sysdep->core_sysdep_network_setopt(itcp_handle->network_handle, CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS,
                &itcp_handle->connect_timeout_ms)) < 0) {
        itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
        return _core_itcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_INVALID_OPTION);
    }

    if (itcp_handle->cred != NULL) {
        res = itcp_handle->sysdep->core_sysdep_network_setopt(itcp_handle->network_handle, CORE_SYSDEP_NETWORK_CRED, itcp_handle->cred);
        if (res < STATE_SUCCESS) {
            itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
            return _core_itcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_INVALID_CRED);
        }
    }

    res = itcp_handle->sysdep->core_sysdep_network_establish(itcp_handle->network_handle);
    if (res < STATE_SUCCESS) {
        itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
        return _core_itcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_EST_FAILED);
    }

    return STATE_SUCCESS;
}

static int32_t _core_itcp_send(core_itcp_handle_t *itcp_handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms)
{
    int32_t res = STATE_SUCCESS;

    if (itcp_handle->network_handle != NULL) {
        res = itcp_handle->sysdep->core_sysdep_network_send(itcp_handle->network_handle, buffer, len, timeout_ms, NULL);
        if (res < STATE_SUCCESS) {
            itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
            core_log(itcp_handle->sysdep, STATE_ITCP_LOG_DISCONNECT, "ITCP network error when sending data, disconnect\r\n");
            res = _core_itcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_SEND_ERR);
        } else if (res != (int32_t)len) {
            res = STATE_SYS_DEPEND_NWK_WRITE_LESSDATA;
        }
    } else {
        res = STATE_SYS_DEPEND_NWK_CLOSED;
    }

    return res;
}

static int32_t _core_itcp_send_body(core_itcp_handle_t *itcp_handle, uint8_t *content, uint32_t len)
{
    int32_t res = STATE_SUCCESS;

    core_log_hexdump(STATE_ITCP_LOG_SEND_CONTENT, '>', content, len);

    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->send_mutex);
    res = _core_itcp_send(itcp_handle, content, len, itcp_handle->send_timeout_ms);
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->send_mutex);

    return res;
}

void *core_itcp_init(void)
{
    core_itcp_handle_t *itcp_handle = NULL;
    aiot_sysdep_portfile_t *sysdep = NULL;

    sysdep = aiot_sysdep_get_portfile();
    if (sysdep == NULL) {
        return NULL;
    }

    itcp_handle = sysdep->core_sysdep_malloc(sizeof(core_itcp_handle_t), CORE_ITCP_MODULE_NAME);
    if (itcp_handle == NULL) {
        return NULL;
    }
    memset(itcp_handle, 0, sizeof(core_itcp_handle_t));

    itcp_handle->sysdep = sysdep;
    itcp_handle->connect_timeout_ms = CORE_ITCP_DEFAULT_CONNECT_TIMEOUT_MS;
    itcp_handle->send_timeout_ms = CORE_ITCP_DEFAULT_SEND_TIMEOUT_MS;
    itcp_handle->recv_timeout_ms = CORE_ITCP_DEFAULT_RECV_TIMEOUT_MS;
    itcp_handle->header_line_max_len = CORE_ITCP_DEFAULT_HEADER_LINE_MAX_LEN;
    itcp_handle->body_buffer_max_len = CORE_ITCP_DEFAULT_BODY_MAX_LEN;
    itcp_handle->deinit_timeout_ms = CORE_ITCP_DEFAULT_DEINIT_TIMEOUT_MS;

    itcp_handle->data_mutex = itcp_handle->sysdep->core_sysdep_mutex_init();
    itcp_handle->send_mutex = itcp_handle->sysdep->core_sysdep_mutex_init();
    itcp_handle->recv_mutex = itcp_handle->sysdep->core_sysdep_mutex_init();

    itcp_handle->core_exec_enabled = 1;

    return itcp_handle;
}

int32_t core_itcp_setopt(void *handle, core_itcp_option_t option, void *data)
{
    int32_t res = STATE_SUCCESS;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL || data == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (option >= CORE_ITCPOPT_MAX) {
        return STATE_USER_INPUT_OUT_RANGE;
    }

    if (itcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_itcp_exec_inc(itcp_handle);

    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->data_mutex);
    switch (option) {
        case CORE_ITCPOPT_ETH: {
            res = core_strdup(itcp_handle->sysdep, &itcp_handle->eth, (char *)data, CORE_ITCP_MODULE_NAME);
        }
        case CORE_ITCPOPT_HOST: {
            res = core_strdup(itcp_handle->sysdep, &itcp_handle->host, (char *)data, CORE_ITCP_MODULE_NAME);
        }
        break;
        case CORE_ITCPOPT_PORT: {
            itcp_handle->port = *(uint16_t *)data;
        }
        break;
        case CORE_ITCPOPT_NETWORK_CRED: {
            if (itcp_handle->cred != NULL) {
                itcp_handle->sysdep->core_sysdep_free(itcp_handle->cred);
                itcp_handle->cred = NULL;
            }
            itcp_handle->cred = itcp_handle->sysdep->core_sysdep_malloc(sizeof(aiot_sysdep_network_cred_t), CORE_ITCP_MODULE_NAME);
            if (itcp_handle->cred != NULL) {
                memset(itcp_handle->cred, 0, sizeof(aiot_sysdep_network_cred_t));
                memcpy(itcp_handle->cred, data, sizeof(aiot_sysdep_network_cred_t));
            } else {
                res = STATE_SYS_DEPEND_MALLOC_FAILED;
            }

        }
        break;
        case CORE_ITCPOPT_CONNECT_TIMEOUT_MS: {
            itcp_handle->connect_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_ITCPOPT_SEND_TIMEOUT_MS: {
            itcp_handle->send_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_ITCPOPT_RECV_TIMEOUT_MS: {
            itcp_handle->recv_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_ITCPOPT_DEINIT_TIMEOUT_MS: {
            itcp_handle->deinit_timeout_ms = *(uint32_t *)data;
        }
        break;
        case CORE_ITCPOPT_HEADER_LINE_MAX_LEN: {
            itcp_handle->header_line_max_len = *(uint32_t *)data;
        }
        break;
        case CORE_ITCPOPT_BODY_BUFFER_MAX_LEN: {
            itcp_handle->body_buffer_max_len = *(uint32_t *)data;
        }
        break;
        case CORE_ITCPOPT_EVENT_HANDLER: {
            itcp_handle->event_handler = (aiot_itcp_event_handler_t)data;
        }
        break;
        case CORE_ITCPOPT_USERDATA: {
            itcp_handle->core_userdata = data;
        }
        break;
        case CORE_ITCPOPT_RECV_HANDLER: {
            itcp_handle->core_recv_handler = (aiot_itcp_recv_handler_t)data;
        }
        break;
        default: {
            res = STATE_USER_INPUT_UNKNOWN_OPTION;
        }
        break;
    }
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->data_mutex);

    _core_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t core_itcp_connect(void *handle)
{
    int32_t res = STATE_SUCCESS;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (itcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_itcp_exec_inc(itcp_handle);

    /* connect to host */
    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->send_mutex);
    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->recv_mutex);
    res = _core_itcp_connect(itcp_handle);
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->recv_mutex);
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->send_mutex);

    _core_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t core_itcp_send(void *handle, const core_itcp_request_t *request)
{
    int32_t res = STATE_SUCCESS;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL || request == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (itcp_handle->host == NULL) {
        return STATE_USER_INPUT_MISSING_HOST;
    }

    if (itcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }

    if (itcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    memset(&itcp_handle->session, 0, sizeof(core_itcp_session_t));

    _core_itcp_exec_inc(itcp_handle);

    /* send itcp content */
    if (request->content != NULL && request->content_len > 0) {
        res = _core_itcp_send_body(itcp_handle, request->content, request->content_len);
        if (res < STATE_SUCCESS) {
            _core_itcp_exec_dec(itcp_handle);
            return res;
        }
    }

    _core_itcp_exec_dec(itcp_handle);

    return res;
}

static void _core_itcp_recv_status_code(core_itcp_handle_t *itcp_handle, uint32_t status_code)
{
    aiot_itcp_recv_t packet;

    if (itcp_handle->core_recv_handler == NULL) {
        return;
    }

    memset(&packet, 0, sizeof(aiot_itcp_recv_t));
    packet.type = AIOT_ITCPRECV_STATUS_CODE;
    packet.data.status_code.code = status_code;

    itcp_handle->core_recv_handler(itcp_handle, &packet, itcp_handle->core_userdata);
}

static int32_t _core_itcp_recv(core_itcp_handle_t *itcp_handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms)
{
    int32_t res = STATE_SUCCESS;

    if (itcp_handle->network_handle != NULL) {
        res = itcp_handle->sysdep->core_sysdep_network_recv(itcp_handle->network_handle, buffer, len, timeout_ms, NULL);
        if (res < STATE_SUCCESS) {
            itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
            core_log(itcp_handle->sysdep, STATE_ITCP_LOG_DISCONNECT, "ITCP network error when receving data, disconnect\r\n");
            res = _core_itcp_sysdep_return(res, STATE_SYS_DEPEND_NWK_RECV_ERR);
        }
    } else {
        res = STATE_SYS_DEPEND_NWK_CLOSED;
    }

    return res;
}

static int32_t _core_itcp_recv_body(core_itcp_handle_t *itcp_handle, uint32_t body_total_len)
{
    int32_t res = STATE_SUCCESS;
    char *buffer = NULL;
    uint32_t idx = 0, buffer_max_len = itcp_handle->body_buffer_max_len;
    uint32_t remaining_len = 0;
    uint64_t timenow_ms = 0;

    buffer = itcp_handle->sysdep->core_sysdep_malloc(buffer_max_len, CORE_ITCP_MODULE_NAME);
    if (buffer == NULL) {
        return STATE_SYS_DEPEND_MALLOC_FAILED;
    }
    memset(buffer, 0, buffer_max_len);

    timenow_ms = itcp_handle->sysdep->core_sysdep_time();
    for (idx = 0; (idx < buffer_max_len) && (itcp_handle->session.body_total_len < 6);) {
        if (timenow_ms > itcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = itcp_handle->sysdep->core_sysdep_time();
        }
        if (itcp_handle->sysdep->core_sysdep_time() - timenow_ms >= itcp_handle->recv_timeout_ms) {
            res =  STATE_ITCP_HEADER_INVALID;
            break;
        }

        /* read itcp header, size (11) in the end */
        if ((res = _core_itcp_recv(itcp_handle, (uint8_t *)&buffer[idx], 1, itcp_handle->recv_timeout_ms)) < STATE_SUCCESS) {
            break;
        }
        idx++;
        if (res == 0 || idx < 6) {
            continue;
        }

        /* body length */
        if (idx > 2) {
            body_total_len = buffer[1] - 2 - 1 - 1 + 2;
        }
        /* next buffer should be itcp response body */
        if (idx >= 6) {
            itcp_handle->session.body_total_len = body_total_len + idx;
            itcp_handle->session.body_read_len = idx;
            break;
        }

        idx = 0;
        memset(buffer, 0, buffer_max_len);
    }

    if (res < STATE_SUCCESS) {
        itcp_handle->sysdep->core_sysdep_free(buffer);
        return res;
    }
    
    if (body_total_len > buffer_max_len) {
        itcp_handle->sysdep->core_sysdep_free(buffer);
        return STATE_ITCP_HEADER_BUFFER_TOO_SHORT;
    }

    remaining_len = itcp_handle->session.body_total_len - itcp_handle->session.body_read_len;
    if (remaining_len == 0) {
        core_log_hexdump(STATE_ITCP_LOG_RECV_FINISH, '<', (uint8_t *)buffer, idx);
        itcp_handle->sysdep->core_sysdep_free(buffer);
        return STATE_ITCP_READ_BODY_FINISHED;
    }

    if (0 == body_total_len) {
        itcp_handle->sysdep->core_sysdep_free(buffer);
        return STATE_ITCP_HEADER_INVALID;
    }

    res = _core_itcp_recv(itcp_handle, (uint8_t *)&buffer[idx], body_total_len, itcp_handle->recv_timeout_ms);
    if (res > 0) {
        aiot_itcp_recv_t packet;

        if (128U > itcp_handle->session.body_total_len) {
            core_log_hexdump(STATE_ITCP_LOG_RECV_CONTENT, '<', (uint8_t *)buffer, itcp_handle->session.body_read_len + res);
        }

        if (itcp_handle->core_recv_handler != NULL) {
            itcp_handle->session.body_read_len += res;
            memset(&packet, 0, sizeof(aiot_itcp_recv_t));
            packet.type = AIOT_ITCPRECV_BODY;
            packet.data.body.buffer = (uint8_t *)buffer;
            packet.data.body.len = itcp_handle->session.body_read_len;

            itcp_handle->core_recv_handler(itcp_handle, &packet, itcp_handle->core_userdata);

            /* sign in status */
            if (0x02 == buffer[5]) {
                uint32_t status_code = 0;
                 status_code = buffer[13];
                 _core_itcp_recv_status_code(itcp_handle, status_code);
            }
        }
    }

    itcp_handle->sysdep->core_sysdep_free(buffer);

    return res;
}

int32_t core_itcp_recv(void *handle)
{
    int32_t res = STATE_SUCCESS;
    uint32_t body_total_len = 0;
    core_itcp_handle_t *itcp_handle = (core_itcp_handle_t *)handle;

    if (itcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (itcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }

    if (itcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_itcp_exec_inc(itcp_handle);

    itcp_handle->session.sm = CORE_ITCP_SM_READ_BODY;

    itcp_handle->sysdep->core_sysdep_mutex_lock(itcp_handle->recv_mutex);
    res = _core_itcp_recv_body(itcp_handle, body_total_len);
    itcp_handle->sysdep->core_sysdep_mutex_unlock(itcp_handle->recv_mutex);
    if (res == STATE_ITCP_READ_BODY_FINISHED || res == STATE_ITCP_HEADER_BUFFER_TOO_SHORT) {
        memset(&itcp_handle->session, 0, sizeof(core_itcp_session_t));
    }

    _core_itcp_exec_dec(itcp_handle);

    return res;
}

int32_t core_itcp_deinit(void **p_handle)
{
    uint32_t deinit_timeout_ms = 0;
    core_itcp_handle_t *itcp_handle = NULL;

    if (p_handle == NULL || *p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    itcp_handle = *(core_itcp_handle_t **)p_handle;

    if (itcp_handle->core_exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    itcp_handle->exec_enabled = 0;
    deinit_timeout_ms = itcp_handle->deinit_timeout_ms;
    do {
        if (itcp_handle->exec_count == 0) {
            break;
        }
        itcp_handle->sysdep->core_sysdep_sleep(CORE_ITCP_DEINIT_INTERVAL_MS);
    } while ((deinit_timeout_ms > CORE_ITCP_DEINIT_INTERVAL_MS) && (deinit_timeout_ms - CORE_ITCP_DEINIT_INTERVAL_MS > 0));

    if (itcp_handle->exec_count != 0) {
        return STATE_ITCP_DEINIT_TIMEOUT;
    }

    if (itcp_handle->network_handle != NULL) {
        itcp_handle->sysdep->core_sysdep_network_deinit(&itcp_handle->network_handle);
    }

    if (itcp_handle->host != NULL) {
        itcp_handle->sysdep->core_sysdep_free(itcp_handle->host);
    }
    if (itcp_handle->cred != NULL) {
        itcp_handle->sysdep->core_sysdep_free(itcp_handle->cred);
    }

    itcp_handle->sysdep->core_sysdep_mutex_deinit(&itcp_handle->data_mutex);
    itcp_handle->sysdep->core_sysdep_mutex_deinit(&itcp_handle->send_mutex);
    itcp_handle->sysdep->core_sysdep_mutex_deinit(&itcp_handle->recv_mutex);

    itcp_handle->sysdep->core_sysdep_free(itcp_handle);

    *p_handle = NULL;

    return STATE_SUCCESS;
}
